<template>
	<div ref="wraper" :class="wraperClasses" @DOMMouseScroll="handleDOMMouseWheel" @mousewheel="handleMouseWheel">
		<div 
			ref="content"
			:class="`${prefix}-content`"
			:style="contentStyles"
			>
			<slot></slot>
		</div>
		<div v-show="percentY < 1 && !disScrollY" :class="scrollYClasses" :style="scrollYStyles">
			<div :class="[`${prefix}-scroll-bar`, `${prefix}-scroll-bar-y`]" :style="scrollBarYStyles" @mousedown="handleMousedownScrollBarY"></div>
		</div>
		<div v-show="percentX < 1 && !disScrollX" :class="scrollXClasses" :style="scrollXStyles">
			<div :class="[`${prefix}-scroll-bar`, `${prefix}-scroll-bar-x`]" :style="scrollBarXStyles" @mousedown="handleMousedownScrollBarX"></div>
		</div>
		<div v-show="percentX < 1 && percentY < 1" :class="`${prefix}-place-holder`"></div>
	</div>
</template>

<script>
/**
 * @author Lison<zhigang.li@tendcloud.com>
 */
export default {
	name: 'scrollBar',
	props: {
		speed: {
			type: Number,
			default: 20
		},
		scrollXStyle: {
			type: Object,
			default () {
				return {};
			}
		},
		disScrollX: {
			type: Boolean,
			default: false
		},
		disScrollY: {
			type: Boolean,
			default: false
		},
		scrollYStyle: {
			type: Object,
			default () {
				return {};
			}
		},
		scrollXType: {
			type: String,
			default: 'cover'
		},
		scrollYType: {
			type: String,
			default: 'cover'
		},
		showAll: {
			type: Boolean,
			default: false
		}
	},
	data () {
		return {
			prefix: 'vue-scroller-bars',
			scrollOffsetX: 0,
			scrollOffsetY: 0,
			contentSize: {
				width: 0,
				height: 0
			},
			wraperSize: {
				width: 0,
				height: 0
			},
			initY: 0,
			initOffsetY: 0,
			initX: 0,
			initOffsetX: 0
		}
	},
	computed: {
		wraperClasses () {
			return [
				`${this.prefix}-wraper`,
				this.showAll ? '' : 'show-when-hover'
			]
		},
		contentStyles () {
			return {
				transform: `translate3d(-${this.scrollOffsetX}px, -${this.scrollOffsetY}px, 0px)`,
				width: this.disScrollX ? '100%' : 'auto',
				height: this.disScrollY ? '100%' : 'auto'
			}
		},
		percentY () {
			return this.wraperSize.height / this.contentSize.height;
		},
		scrollYStyles () {
			return Object.assign(this.scrollYStyle, {
				height: this.percentX < 1 ? 'calc(100% - 14px)' : '100%'
			});
		},
		scrollBarYHeight () {
			return this.percentY * (this.wraperSize.height - 2);
		},
		scrollBarYStyles () {
			let height = this.scrollBarYHeight;
			return {
				transform: `translate3d(0px, ${this.scrollOffsetY * (height / this.wraperSize.height)}px, 0px)`,
				height: `${height}px`
			}
		},
		scrollYClasses () {
			return [
				`${this.prefix}-scroll`,
				`${this.prefix}-scroll-y`,
				this.scrollYType === 'cover' ? 'scroll-y-cover' : ''
			]
		},
		gapY () {
			return this.contentSize.height - this.wraperSize.height;
		},
		percentX () {
			return this.wraperSize.width / this.contentSize.width;
		},
		scrollXStyles () {
			return Object.assign(this.scrollXStyle, {
				width: this.percentY < 1 ? 'calc(100% - 14px)' : '100%'
			});
		},
		scrollBarXWidth () {
			return this.percentX * (this.wraperSize.width - 2);
		},
		scrollBarXStyles () {
			let width = this.scrollBarXWidth;
			return {
				transform: `translate3d(${this.scrollOffsetX * (width / this.wraperSize.width)}px, 0px, 0px)`,
				width: `${width}px`
			}
		},
		scrollXClasses () {
			return [
				`${this.prefix}-scroll`,
				`${this.prefix}-scroll-x`,
				this.scrollXType === 'cover' ? 'scroll-x-cover' : ''
			]
		},
		gapX () {
			return this.contentSize.width - this.wraperSize.width;
		}
	},
	methods: {
		resize () {
			this.$nextTick(() => {
				let wraperRect = this.$refs.wraper.getBoundingClientRect();
				let contentRect = this.$refs.content.getBoundingClientRect();
				this.contentSize = {
					width: contentRect.width,
					height: contentRect.height
				};
				let percentXLowerThanOne = (wraperRect.width / contentRect.width) < 1;
				let percentYLowerThanOne = (wraperRect.height / contentRect.height) < 1;
				let gap = percentXLowerThanOne && percentYLowerThanOne ? 14 : 0;
				this.wraperSize = {
					width: wraperRect.width - gap,
					height: wraperRect.height - gap
				};
				if (this.contentSize.height <= this.wraperSize.height) { // if content's height shorter than wraper's height
					this.scrollOffsetY = 0;
				};
				if ((wraperRect.bottom > contentRect.bottom) && (this.scrollOffsetY > 0)) { // if content's bottom upper wraper's bottom
					this.scrollOffsetY += contentRect.bottom - wraperRect.bottom
				};
			})
		},
		handleMouseWheel (e) {
			this.scrollOffsetY += this.percentY < 1 ? e.deltaY : 0;
			this.scrollOffsetX += this.percentX < 1 ? e.deltaX : 0;
			if (this.percentY < 1) {
				if (this.scrollOffsetY >= this.gapY) {
					this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
				} else if (this.scrollOffsetY <= 0) {
					this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
				}
			}
			if (this.percentX < 1) {
				if (this.scrollOffsetX >= this.gapX) {
					this.scrollOffsetX = Math.min(this.gapX, this.scrollOffsetX);
				} else if (this.scrollOffsetX <= 0) {
					this.scrollOffsetX = Math.max(this.scrollOffsetX, 0);
				}
			}
		},
		handleDOMMouseWheel (e) {
			this.scrollOffsetY += e.detail * 16;
			if (this.scrollOffsetY >= this.gapY) {
				this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
			} else if (this.scrollOffsetY <= 0) {
				this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
			}
		},
		handleMousemoveY (e) {
			let offset = e.pageY - this.initY;
			this.scrollOffsetY = this.initOffsetY + offset / ((this.wraperSize.height - 2 - this.scrollBarYHeight) / (this.contentSize.height - this.wraperSize.height));
			if (this.scrollOffsetY >= this.gapY) {
				this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
			} else if (this.scrollOffsetY <= 0) {
				this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
			}
			e.preventDefault();
		},
		handleMousedownScrollBarY (e) {
			this.initY = e.pageY;
			this.initOffsetY = this.scrollOffsetY;
			document.addEventListener('mousemove', this.handleMousemoveY);
			document.addEventListener('mouseup', this.handleMouseup);
		},
		handleMousemoveX (e) {
			let offset = e.pageX - this.initX;
			this.scrollOffsetX = this.initOffsetX + offset / ((this.wraperSize.width - 2 - this.scrollBarXWidth) / (this.contentSize.width - this.wraperSize.width));
			if (this.scrollOffsetX >= this.gapX) {
				this.scrollOffsetX = Math.min(this.gapX, this.scrollOffsetX);
			} else if (this.scrollOffsetX <= 0) {
				this.scrollOffsetX = Math.max(this.scrollOffsetX, 0);
			}
			e.preventDefault();
		},
		handleMousedownScrollBarX (e) {
			this.initX = e.pageX;
			this.initOffsetX = this.scrollOffsetX;
			document.addEventListener('mousemove', this.handleMousemoveX);
			document.addEventListener('mouseup', this.handleMouseup);
		},
		handleMouseup (e) {
			document.removeEventListener('mousemove', this.handleMousemoveY);
			document.removeEventListener('mousemove', this.handleMousemoveX);
			document.removeEventListener('mousemove', this.handleMouseup);
		},
	},
	mounted () {
		this.resize();
	}
}
</script>

<style lang="less">
@import './scroll-bar.less';
</style>
